/*
 *   Copyright 2021 zzh
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 *
 */

package com.aduib.boot.common.util;


import com.aduib.boot.common.concurrent.internal.LongCounter;
import com.aduib.boot.common.log.LOG;
import org.jctools.util.Pow2;
import sun.misc.Unsafe;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAdder;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @description: SystemUtils
 * @author: zzh
 * @date: 2021/10/29 10:35
 */
public final class SystemUtils {

  public static final SystemUtils INSTANCE = new SystemUtils();

  private static final Pattern MAX_DIRECT_MEMORY_SIZE_ARG_PATTERN =
          Pattern.compile("\\s*-XX:MaxDirectMemorySize\\s*=\\s*([0-9]+)\\s*([kKmMgG]?)\\s*$");

  private static final long MAX_DIRECT_MEMORY = maxDirectMemory();

  private static final long ADDRESS_FIELD_OFFSET;
  private static final long BYTE_ARRAY_BASE_OFFSET;
  private static final long INT_ARRAY_BASE_OFFSET;
  private final boolean IS_OS_AIX = getOSMatches("AIX");
  private final boolean IS_OS_HP_UX = getOSMatches("HP-UX");
  private final boolean IS_OS_IRIX = getOSMatches("Irix");
  private final boolean IS_OS_LINUX = getOSMatches("Linux") || getOSMatches("LINUX");
  private final boolean IS_OS_MAC = getOSMatches("Mac");
  private final boolean IS_OS_MAC_OSX = getOSMatches("Mac OS X");
  private final boolean IS_OS_OS2 = getOSMatches("OS/2");
  private final boolean IS_OS_SOLARIS = getOSMatches("Solaris");
  private final boolean IS_OS_SUN_OS = getOSMatches("SunOS");
  private final boolean IS_OS_WINDOWS = getOSMatches("Windows");
  private final boolean IS_JAVA_1_6 = getJavaVersionMatches("1.6");
  private final boolean IS_JAVA_1_7 = getJavaVersionMatches("1.7");
  private final boolean IS_JAVA_1_8 = getJavaVersionMatches("1.8");
  private final boolean IS_JAVA_9 = getJavaVersionMatches("9");
  private final boolean IS_JAVA_10 = getJavaVersionMatches("10");
  private final boolean IS_JAVA_11 = getJavaVersionMatches("11");
  private final boolean IS_JAVA_12 = getJavaVersionMatches("12");

  private static final int JAVA_VERSION_NUMBER = javaVersion0();

  private static final boolean IS_ANDROID = isAndroid0();

  private static final Method ALIGN_SLICE;

  private static final Cleaner CLEANER;

  private static final boolean USE_DIRECT_BUFFER_NO_CLEANER;

  private static final Constructor<?> DIRECT_BUFFER_CONSTRUCTOR;

  private static final AtomicLong DIRECT_MEMORY_COUNTER;

  static final Unsafe UNSAFE;


  private static final long INT_ARRAY_INDEX_SCALE;
  private static final long LONG_ARRAY_BASE_OFFSET;
  private static final long LONG_ARRAY_INDEX_SCALE;

  private static final Cleaner NOOP = buffer -> {

  };

  static {
    final ByteBuffer direct = ByteBuffer.allocateDirect(1);
    Field addressField = null;
    Throwable unsafeUnavailabilityCause = null;
    Unsafe unsafe;
    Object internalUnsafe = null;

    // attempt to access field Unsafe#theUnsafe
    final Object maybeUnsafe =
            AccessController.doPrivileged(
            (PrivilegedAction<Object>)
                () -> {
                  try {
                    final Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
                    Throwable cause = ClassUtils.trySetAccessible(unsafeField, false);
                    if (cause != null) {
                      return cause;
                    }
                    return unsafeField.get(null);
                  } catch (NoSuchFieldException
                      | SecurityException
                      | IllegalAccessException
                      | NoClassDefFoundError e) {
                    return e;
                  } // Also catch NoClassDefFoundError in case someone uses for example OSGI and it
                });

    if (maybeUnsafe instanceof Throwable) {
      unsafe = null;
      unsafeUnavailabilityCause = (Throwable) maybeUnsafe;
      LOG.d("sun.misc.Unsafe.theUnsafe: unavailable: {}", unsafeUnavailabilityCause);
    } else {
      unsafe = (Unsafe) maybeUnsafe;
      LOG.d("sun.misc.Unsafe.theUnsafe: available");
    }

    if (Objects.isNull(unsafe) && javaVersion() >= 9) {
      Object maybeException =
          AccessController.doPrivileged(
              (PrivilegedAction<Object>)
                  () -> {
                    try {
                      Class<?> internalUnsafeClass =
                          ClassUtils.getDefaultClassLoader(SystemUtils.class)
                              .loadClass("jdk.internal.misc.Unsafe");
                      Method method = internalUnsafeClass.getDeclaredMethod("getUnsafe");
                      return method.invoke(null);
                    } catch (Throwable e) {
                      return e;
                    }
                  });
      if (!(maybeException instanceof Throwable)) {
        internalUnsafe = maybeException;
        LOG.d("sun.misc.Unsafe.theUnsafe: unavailable: {}", internalUnsafe);
      } else {
        unsafe = (Unsafe) internalUnsafe;
        LOG.d("sun.misc.Unsafe.theUnsafe: available");
      }
    }

    if (unsafe != null) {
      final Unsafe finalUnsafe = unsafe;

      final Object maybeAddressField =
          AccessController.doPrivileged(
              (PrivilegedAction<Object>)
                  () -> {
                    try {
                      final Field field = Buffer.class.getDeclaredField("address");
                      final long offset = finalUnsafe.objectFieldOffset(field);
                      final long address = finalUnsafe.getLong(direct, offset);

                      if (address == 0) {
                        return null;
                      }
                      return field;
                    } catch (NoSuchFieldException | SecurityException e) {
                      return e;
                    }
                  });

      if (maybeAddressField instanceof Field) {
        addressField = (Field) maybeAddressField;
        LOG.d("java.nio.Buffer.address: available");
      } else {
        unsafeUnavailabilityCause = (Throwable) maybeAddressField;
        LOG.d("java.nio.Buffer.address: unavailable", (Throwable) unsafeUnavailabilityCause);
        unsafe = null;
      }
    }

    UNSAFE = unsafe;

    if (unsafe == null) {
      ADDRESS_FIELD_OFFSET = -1;
      BYTE_ARRAY_BASE_OFFSET = -1;
      LONG_ARRAY_BASE_OFFSET = -1;
      LONG_ARRAY_INDEX_SCALE = -1;
      INT_ARRAY_BASE_OFFSET = -1;
      INT_ARRAY_INDEX_SCALE = -1;
      DIRECT_BUFFER_CONSTRUCTOR = null;
    } else {

      Constructor<?> directBufferConstructor;
      long address = -1;
      try {
        final Object maybeDirectBufferConstructor =
                AccessController.doPrivileged(new PrivilegedAction<Object>() {
                  @Override
                  public Object run() {
                    try {
                      final Constructor<?> constructor =
                              direct.getClass().getDeclaredConstructor(long.class, int.class);
                      Throwable cause = ClassUtils.trySetAccessible(constructor, true);
                      if (cause != null) {
                        return cause;
                      }
                      return constructor;
                    } catch (NoSuchMethodException e) {
                      return e;
                    } catch (SecurityException e) {
                      return e;
                    }
                  }
                });

        if (maybeDirectBufferConstructor instanceof Constructor<?>) {
          address = UNSAFE.allocateMemory(1);
          // try to use the constructor now
          try {
            ((Constructor<?>) maybeDirectBufferConstructor).newInstance(address, 1);
            directBufferConstructor = (Constructor<?>) maybeDirectBufferConstructor;
            LOG.d("direct buffer constructor: available");
          } catch (InstantiationException e) {
            directBufferConstructor = null;
          } catch (IllegalAccessException e) {
            directBufferConstructor = null;
          } catch (InvocationTargetException e) {
            directBufferConstructor = null;
          }
        } else {
          LOG.d("direct buffer constructor: unavailable: {}",
                  ((Throwable) maybeDirectBufferConstructor).getMessage());
          directBufferConstructor = null;
        }
      } finally {
        if (address != -1) {
          UNSAFE.freeMemory(address);
        }
      }
      DIRECT_BUFFER_CONSTRUCTOR = directBufferConstructor;

      ADDRESS_FIELD_OFFSET = objectFieldOffset(addressField);
      BYTE_ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset(byte[].class);
      INT_ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset(int[].class);
      INT_ARRAY_INDEX_SCALE = UNSAFE.arrayIndexScale(int[].class);
      LONG_ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset(long[].class);
      LONG_ARRAY_INDEX_SCALE = UNSAFE.arrayIndexScale(long[].class);
    }

    if (javaVersion() > 9) {
      ALIGN_SLICE =
          (Method)
              AccessController.doPrivileged(
                  new PrivilegedAction<Object>() {
                    @Override
                    public Object run() {
                      try {
                        return ByteBuffer.class.getDeclaredMethod("alignedSlice", int.class);
                      } catch (Exception e) {
                        return null;
                      }
                    }
                  });
    } else {
      ALIGN_SLICE = null;
    }

    if (!hasUnsafe() || !hasDirectBufferNoCleanerConstructor()) {
      USE_DIRECT_BUFFER_NO_CLEANER = false;
      DIRECT_MEMORY_COUNTER = null;
    } else {
      USE_DIRECT_BUFFER_NO_CLEANER = true;
      if (MAX_DIRECT_MEMORY < 0) {
        DIRECT_MEMORY_COUNTER = null;
      } else {
        DIRECT_MEMORY_COUNTER = new AtomicLong();
      }
    }

    CLEANER = CleanerJava9.isSupported() ? new CleanerJava9() : NOOP;
  }

  private final String USER_HOME = SystemUtils.getStringValue("user.home", false);
  private final String OS_NAME = SystemUtils.getStringValue("os.name", false);
  private final String JAVA_VERSION = SystemUtils.getStringValue("java.version", false);

  /**
   * 取得系统属性，如果因为Java安全的限制而失败，则将错误打在Log中，然后返回 defaultValue
   *
   * @param name 属性名
   * @param defaultValue 默认值
   * @return 属性值或defaultValue
   * @see System#getProperty(String)
   * @see System#getenv(String)
   */
  public static String getStringValue(String name, String defaultValue) {
    return StrUtils.nullToDefault(getStringValue(name, false), defaultValue);
  }

  /**
   * 取得系统属性，如果因为Java安全的限制而失败，则将错误打在Log中，然后返回 {@code null}
   *
   * @param name 属性名
   * @param quiet 安静模式，不将出错信息打在<code>System.err</code>中
   * @return 属性值或{@code null}
   * @see System#getProperty(String)
   * @see System#getenv(String)
   */
  public static String getStringValue(String name, boolean quiet) {
    String value = null;
    try {
      value = System.getProperty(name);
    } catch (SecurityException e) {
      if (false == quiet) {
        LOG.e(
            "Caught a SecurityException reading the system property '{}'; "
                + "the SystemUtil property value will default to null.",
            name);
      }
    }

    if (null == value) {
      try {
        value = System.getenv(name);
      } catch (SecurityException e) {
        if (false == quiet) {
          LOG.e(
              "Caught a SecurityException reading the system env '{}'; "
                  + "the SystemUtil env value will default to null.",
              name);
        }
      }
    }

    return value;
  }

  /**
   * 获得System属性
   *
   * @param key 键
   * @return 属性值
   * @see System#getProperty(String)
   * @see System#getenv(String)
   */
  public static String getStringValue(String key) {
    return getStringValue(key, null);
  }

  /**
   * 判断当前OS的类型。
   *
   * <p>如果不能取得系统属性<code>os.name</code>（因为Java安全限制），则总是返回<code>false</code>
   *
   * @return 如果当前OS类型为AIX，则返回<code>true</code>
   */
  public final boolean isAix() {
    return IS_OS_AIX;
  }

  /**
   * 判断当前OS的类型。
   *
   * <p>如果不能取得系统属性<code>os.name</code>（因为Java安全限制），则总是返回<code>false</code>
   *
   * @return 如果当前OS类型为HP-UX，则返回<code>true</code>
   */
  public final boolean isHpUx() {
    return IS_OS_HP_UX;
  }

  /**
   * 判断当前OS的类型。
   *
   * <p>如果不能取得系统属性<code>os.name</code>（因为Java安全限制），则总是返回<code>false</code>
   *
   * @return 如果当前OS类型为IRIX，则返回<code>true</code>
   */
  public final boolean isIrix() {
    return IS_OS_IRIX;
  }

  /**
   * 判断当前OS的类型。
   *
   * <p>如果不能取得系统属性<code>os.name</code>（因为Java安全限制），则总是返回<code>false</code>
   *
   * @return 如果当前OS类型为Linux，则返回<code>true</code>
   */
  public final boolean isLinux() {
    return IS_OS_LINUX;
  }

  /**
   * 判断当前OS的类型。
   *
   * <p>如果不能取得系统属性<code>os.name</code>（因为Java安全限制），则总是返回<code>false</code>
   *
   * @return 如果当前OS类型为Mac，则返回<code>true</code>
   */
  public final boolean isMac() {
    return IS_OS_MAC;
  }

  /**
   * 判断当前OS的类型。
   *
   * <p>如果不能取得系统属性<code>os.name</code>（因为Java安全限制），则总是返回<code>false</code>
   *
   * @return 如果当前OS类型为MacOS X，则返回<code>true</code>
   */
  public final boolean isMacOsX() {
    return IS_OS_MAC_OSX;
  }

  /**
   * 判断当前OS的类型。
   *
   * <p>如果不能取得系统属性<code>os.name</code>（因为Java安全限制），则总是返回<code>false</code>
   *
   * @return 如果当前OS类型为OS2，则返回<code>true</code>
   */
  public final boolean isOs2() {
    return IS_OS_OS2;
  }

  /**
   * 判断当前OS的类型。
   *
   * <p>如果不能取得系统属性<code>os.name</code>（因为Java安全限制），则总是返回<code>false</code>
   *
   * @return 如果当前OS类型为Solaris，则返回<code>true</code>
   */
  public final boolean isSolaris() {
    return IS_OS_SOLARIS;
  }

  /**
   * 判断当前OS的类型。
   *
   * <p>如果不能取得系统属性<code>os.name</code>（因为Java安全限制），则总是返回<code>false</code>
   *
   * @return 如果当前OS类型为Sun OS，则返回<code>true</code>
   */
  public final boolean isSunOS() {
    return IS_OS_SUN_OS;
  }

  /**
   * 判断当前OS的类型。
   *
   * <p>如果不能取得系统属性<code>os.name</code>（因为Java安全限制），则总是返回<code>false</code>
   *
   * @return 如果当前OS类型为Windows，则返回<code>true</code>
   */
  public final boolean isWindows() {
    return IS_OS_WINDOWS;
  }

  /**
   * 匹配OS名称。
   *
   * @param osNamePrefix OS名称前缀
   * @return 如果匹配，则返回<code>true</code>
   */
  private boolean getOSMatches(String osNamePrefix) {
    if (OS_NAME == null) {
      return false;
    }

    return OS_NAME.startsWith(osNamePrefix);
  }

  /**
   * 判断当前Java的版本。
   *
   * <p>如果不能取得系统属性<code>java.version</code>（因为Java安全限制），则总是返回 <code>false</code>
   *
   * @return 如果当前Java版本为1.6，则返回<code>true</code>
   */
  public final boolean isJava1_6() {
    return IS_JAVA_1_6;
  }

  /**
   * 判断当前Java的版本。
   *
   * <p>如果不能取得系统属性<code>java.version</code>（因为Java安全限制），则总是返回 <code>false</code>
   *
   * @return 如果当前Java版本为1.7，则返回<code>true</code>
   */
  public final boolean isJava1_7() {
    return IS_JAVA_1_7;
  }

  /**
   * 判断当前Java的版本。
   *
   * <p>如果不能取得系统属性<code>java.version</code>（因为Java安全限制），则总是返回 <code>false</code>
   *
   * @return 如果当前Java版本为1.8，则返回<code>true</code>
   */
  public final boolean isJava1_8() {
    return IS_JAVA_1_8;
  }

  /**
   * 判断当前Java的版本。
   *
   * <p>如果不能取得系统属性<code>java.version</code>（因为Java安全限制），则总是返回 <code>false</code>
   *
   * @return 如果当前Java版本为9，则返回<code>true</code>
   */
  public final boolean isJava9() {
    return IS_JAVA_9;
  }

  /**
   * 判断当前Java的版本。
   *
   * <p>如果不能取得系统属性<code>java.version</code>（因为Java安全限制），则总是返回 <code>false</code>
   *
   * @return 如果当前Java版本为10，则返回<code>true</code>
   */
  public final boolean isJava10() {
    return IS_JAVA_10;
  }

  /**
   * 判断当前Java的版本。
   *
   * <p>如果不能取得系统属性<code>java.version</code>（因为Java安全限制），则总是返回 <code>false</code>
   *
   * @return 如果当前Java版本为11，则返回<code>true</code>
   */
  public final boolean isJava11() {
    return IS_JAVA_11;
  }

  /**
   * 判断当前Java的版本。
   *
   * <p>如果不能取得系统属性<code>java.version</code>（因为Java安全限制），则总是返回 <code>false</code>
   *
   * @return 如果当前Java版本为12，则返回<code>true</code>
   */
  public final boolean isJava12() {
    return IS_JAVA_12;
  }

  /**
   * 匹配当前Java的版本。
   *
   * @param versionPrefix Java版本前缀
   * @return 如果版本匹配，则返回<code>true</code>
   */
  private boolean getJavaVersionMatches(String versionPrefix) {
    if (JAVA_VERSION == null) {
      return false;
    }

    return JAVA_VERSION.startsWith(versionPrefix);
  }

  /**
   * Returns the value of the Java system property with the specified {@code key}, while falling
   * back to the specified default value if the property access fails.
   *
   * @return the property value. {@code def} if there's no such property or if an access to the
   *     specified property is not allowed.
   */
  public static boolean getBooleanValue(String key, boolean def) {
    String value = getStringValue(key);
    if (value == null) {
      return def;
    }

    value = value.trim().toLowerCase();
    if (value.isEmpty()) {
      return def;
    }

    if ("true".equals(value) || "yes".equals(value) || "1".equals(value)) {
      return true;
    }

    if ("false".equals(value) || "no".equals(value) || "0".equals(value)) {
      return false;
    }

    LOG.w(
        "Unable to parse the boolean system property '{}':{} - using the default value: {}",
        key,
        value,
        def);

    return def;
  }

  /**
   * Returns the value of the Java system property with the specified {@code key}, while falling
   * back to the specified default value if the property access fails.
   *
   * @return the property value. {@code def} if there's no such property or if an access to the
   *     specified property is not allowed.
   */
  public static int getIntValue(String key, int def) {
    String value = getStringValue(key);
    if (value == null) {
      return def;
    }

    value = value.trim();
    try {
      return Integer.parseInt(value);
    } catch (Exception e) {
      // Ignore
    }

    LOG.w(
        "Unable to parse the integer system property '{}':{} - using the default value: {}",
        key,
        value,
        def);

    return def;
  }

  /**
   * Returns the value of the Java system property with the specified {@code key}, while falling
   * back to the specified default value if the property access fails.
   *
   * @return the property value. {@code def} if there's no such property or if an access to the
   *     specified property is not allowed.
   */
  public static long getLongValue(String key, long def) {
    String value = getStringValue(key);
    if (value == null) {
      return def;
    }

    value = value.trim();
    try {
      return Long.parseLong(value);
    } catch (Exception e) {
      // Ignore
    }

    LOG.w(
        "Unable to parse the long integer system property '{}':{} - using the default value: {}",
        key,
        value,
        def);

    return def;
  }

  private static boolean isAndroid0() {
    // Idea: Sometimes java binaries include Android classes on the classpath, even if it isn't
    // actually Android.
    // Rather than check if certain classes are present, just check the VM, which is tied to the
    // JDK.

    // Optional improvement: check if `android.os.Build.VERSION` is >= 24. On later versions of
    // Android, the
    // OpenJDK is used, which means `Unsafe` will actually work as expected.

    // Android sets this property to Dalvik, regardless of whether it actually is.
    String vmName = getStringValue("java.vm.name");
    boolean isAndroid = "Dalvik".equals(vmName);
    if (isAndroid) {
      LOG.d("Platform: Android");
    }
    return isAndroid;
  }

  public static boolean isAndroid() {
    return IS_ANDROID;
  }

  private static int javaVersion0() {
    final int majorVersion;

    if (isAndroid0()) {
      majorVersion = 6;
    } else {
      majorVersion = majorVersionFromJavaSpecificationVersion();
    }

    //    LOG.d("Java version: {}", majorVersion);

    return majorVersion;
  }

  public static int javaVersion() {
    return JAVA_VERSION_NUMBER;
  }

  public static boolean hasUnsafe() {
    return UNSAFE != null;
  }

  // Package-private for testing only
  static int majorVersionFromJavaSpecificationVersion() {
    return majorVersion(getStringValue("java.specification.version", "1.6"));
  }

  // Package-private for testing only
  static int majorVersion(final String javaSpecVersion) {
    final String[] components = javaSpecVersion.split("\\.");
    final int[] version = new int[components.length];
    for (int i = 0; i < components.length; i++) {
      version[i] = Integer.parseInt(components[i]);
    }

    if (version[0] == 1) {
      assert version[1] >= 6;
      return version[1];
    } else {
      return version[0];
    }
  }

  /**
   * 取得当前登录用户的home目录（取自系统属性：<code>user.home</code>）。
   *
   * <p>例如：<code>"/home/admin"</code>
   *
   * @return 属性值，如果不能取得（因为Java安全限制）或值不存在，则返回<code>null</code>。
   */
  public final String getHomeDir() {
    return USER_HOME;
  }

  public static int availableProcessors() {
    return Runtime.getRuntime().availableProcessors();
  }

  public static long maxDirectMemory() {
    long maxDirectMemory = 0;

    ClassLoader systemClassLoader = null;
    try {
      systemClassLoader = ClassUtils.getSystemClassLoader();

      // When using IBM J9 / Eclipse OpenJ9 we should not use VM.maxDirectMemory() as it not
      // reflects the
      // correct value.
      // See:
      //  - https://github.com/netty/netty/issues/7654
      String vmName = getStringValue("java.vm.name", "").toLowerCase();
      if (!vmName.startsWith("ibm j9")
          &&
          // https://github.com/eclipse/openj9/blob/openj9-0.8.0/runtime/include/vendor_version.h#L53
          !vmName.startsWith("eclipse openj9")) {
        // Try to get from sun.misc.VM.maxDirectMemory() which should be most accurate.
        Class<?> vmClass = Class.forName("sun.misc.VM", true, systemClassLoader);
        Method m = vmClass.getDeclaredMethod("maxDirectMemory");
        maxDirectMemory = ((Number) m.invoke(null)).longValue();
      }
    } catch (Throwable ignored) {
      // Ignore
    }

    if (maxDirectMemory > 0) {
      return maxDirectMemory;
    }

    try {
      // Now try to get the JVM option (-XX:MaxDirectMemorySize) and parse it.
      // Note that we are using reflection because Android doesn't have these classes.
      Class<?> mgmtFactoryClass =
          Class.forName("java.lang.management.ManagementFactory", true, systemClassLoader);
      Class<?> runtimeClass =
          Class.forName("java.lang.management.RuntimeMXBean", true, systemClassLoader);

      Object runtime = mgmtFactoryClass.getDeclaredMethod("getRuntimeMXBean").invoke(null);

      @SuppressWarnings("unchecked")
      List<String> vmArgs =
          (List<String>) runtimeClass.getDeclaredMethod("getInputArguments").invoke(runtime);
      for (int i = vmArgs.size() - 1; i >= 0; i--) {
        Matcher m = MAX_DIRECT_MEMORY_SIZE_ARG_PATTERN.matcher(vmArgs.get(i));
        if (!m.matches()) {
          continue;
        }

        maxDirectMemory = Long.parseLong(m.group(1));
        switch (m.group(2).charAt(0)) {
          case 'k':
          case 'K':
            maxDirectMemory *= 1024;
            break;
          case 'm':
          case 'M':
            maxDirectMemory *= 1024 * 1024;
            break;
          case 'g':
          case 'G':
            maxDirectMemory *= 1024 * 1024 * 1024;
            break;
        }
        break;
      }
    } catch (Throwable ignored) {
      // Ignore
    }

    if (maxDirectMemory <= 0) {
      maxDirectMemory = Runtime.getRuntime().maxMemory();
      LOG.d("maxDirectMemory: {} bytes (maybe)", maxDirectMemory);
    } else {
      LOG.d("maxDirectMemory: {} bytes", maxDirectMemory);
    }

    return maxDirectMemory;
  }

  public static ByteBuffer alignDirectBuffer(ByteBuffer buffer, int alignment) {
    if (!buffer.isDirect()) {
      throw new IllegalArgumentException("Cannot get aligned slice of non-direct byte buffer.");
    }
    if (hasAlignSliceMethod()) {
      return alignSlice(buffer, alignment);
    }
    if (hasUnsafe()) {
      long address = directBufferAddress(buffer);
      long aligned = Pow2.align(address, alignment);
      buffer.position((int) (aligned - address));
      return buffer.slice();
    }
    // We don't have enough information to be able to align any buffers.
    throw new UnsupportedOperationException(
        "Cannot align direct buffer. "
            + "Needs either Unsafe or ByteBuffer.alignSlice method available.");
  }

  public static LongCounter newLongCounter() {
    if (javaVersion() >= 8) {
      return new LongAdderCounter();
    } else {
      return new AtomicLongCounter();
    }
  }

  /**
   * 获取属性的偏移量
   *
   * @param field
   * @return
   */
  public static long objectFieldOffset(Field field) {
    return AccessController.doPrivileged(
            (PrivilegedAction<Long>) () -> UNSAFE.objectFieldOffset(field));
  }

  public static ByteBuffer allocateDirectNoCleaner(int capacity) {
    try {
      return allocateDirectNoCleaner0(capacity);
    } catch (Throwable e) {
      throw e;
    }
  }

  public static long directBufferAddress(ByteBuffer buffer) {
    return AccessController.doPrivileged(
            (PrivilegedAction<Long>) () -> getLong(buffer, ADDRESS_FIELD_OFFSET));
  }

  public static boolean useDirectBufferNoCleaner() {
    return CLEANER == NOOP;
  }

  public static void freeDirectNoCleaner(ByteBuffer byteBuffer) {
    freeMemory(directBufferAddress(byteBuffer));
  }

  public static void freeDirectBuffer(ByteBuffer byteBuffer) {
    CLEANER.freeDirectBuffer(byteBuffer);
  }

  public static long byteArrayBaseOffset() {
    return AccessController.doPrivileged((PrivilegedAction<Long>) () -> BYTE_ARRAY_BASE_OFFSET);
  }

  public static Object getObject(Object object, long fieldOffset) {
    return AccessController.doPrivileged(
        (PrivilegedAction<Object>) () -> UNSAFE.getObject(object, fieldOffset));
  }

  public static byte getByte(Object object, long fieldOffset) {
    return AccessController.doPrivileged(
        (PrivilegedAction<Byte>) () -> UNSAFE.getByte(object, fieldOffset));
  }

  public static short getShort(Object object, long fieldOffset) {
    return UNSAFE.getShort(object, fieldOffset);
  }

  public static char getChar(Object object, long fieldOffset) {
    return AccessController.doPrivileged(
        (PrivilegedAction<Character>) () -> UNSAFE.getChar(object, fieldOffset));
  }

  public static int getInt(Object object, long fieldOffset) {
    return AccessController.doPrivileged(
        (PrivilegedAction<Integer>) () -> UNSAFE.getInt(object, fieldOffset));
  }

  public static float getFloat(Object object, long fieldOffset) {
    return AccessController.doPrivileged(
        (PrivilegedAction<Float>) () -> UNSAFE.getFloat(object, fieldOffset));
  }

  public static long getLong(Object object, long fieldOffset) {
    return AccessController.doPrivileged(
        (PrivilegedAction<Long>) () -> UNSAFE.getLong(object, fieldOffset));
  }

  public static double getDouble(Object object, long fieldOffset) {
    return AccessController.doPrivileged(
        (PrivilegedAction<Double>) () -> UNSAFE.getDouble(object, fieldOffset));
  }

  public static byte getByte(long address) {
    return AccessController.doPrivileged((PrivilegedAction<Byte>) () -> UNSAFE.getByte(address));
  }

  public static short getShort(long address) {
    return AccessController.doPrivileged((PrivilegedAction<Short>) () -> UNSAFE.getShort(address));
  }

  public static int getIntValue(long address) {
    return AccessController.doPrivileged((PrivilegedAction<Integer>) () -> UNSAFE.getInt(address));
  }

  public static long getLong(long address) {
    return AccessController.doPrivileged((PrivilegedAction<Long>) () -> UNSAFE.getLong(address));
  }

  public static byte getByte(byte[] data, int index) {
    return AccessController.doPrivileged(
        (PrivilegedAction<Byte>) () -> UNSAFE.getByte(data, BYTE_ARRAY_BASE_OFFSET + index));
  }

  public static byte getByte(byte[] data, long index) {
    return AccessController.doPrivileged(
        (PrivilegedAction<Byte>) () -> UNSAFE.getByte(data, BYTE_ARRAY_BASE_OFFSET + index));
  }

  public static short getShort(byte[] data, int index) {
    return AccessController.doPrivileged(
        (PrivilegedAction<Short>) () -> UNSAFE.getShort(data, BYTE_ARRAY_BASE_OFFSET + index));
  }

  public static int getInt(byte[] data, int index) {
    return AccessController.doPrivileged(
        (PrivilegedAction<Integer>) () -> UNSAFE.getInt(data, BYTE_ARRAY_BASE_OFFSET + index));
  }

  public static int getInt(int[] data, long index) {
    return AccessController.doPrivileged(
        (PrivilegedAction<Integer>)
            () -> UNSAFE.getInt(data, INT_ARRAY_BASE_OFFSET + INT_ARRAY_INDEX_SCALE * index));
  }

  public static int getIntVolatile(long address) {
    return AccessController.doPrivileged(
        (PrivilegedAction<Integer>) () -> UNSAFE.getIntVolatile(null, address));
  }

  public static void putIntOrdered(long adddress, int newValue) {
    AccessController.doPrivileged(
        new PrivilegedAction<Void>() {
          @Override
          public Void run() {
            UNSAFE.putOrderedInt(null, adddress, newValue);
            return null;
          }
        });
  }

  public static long getLong(byte[] data, int index) {
    return AccessController.doPrivileged(
            (PrivilegedAction<Long>) () -> UNSAFE.getLong(data, BYTE_ARRAY_BASE_OFFSET + index));
  }

  public static long getLong(long[] data, long index) {
    return AccessController.doPrivileged(
            (PrivilegedAction<Long>)
                    () -> UNSAFE.getLong(data, LONG_ARRAY_BASE_OFFSET + LONG_ARRAY_INDEX_SCALE * index));
  }

  static boolean hasDirectBufferNoCleanerConstructor() {
    return DIRECT_BUFFER_CONSTRUCTOR != null;
  }

  static boolean hasAlignSliceMethod() {
    return ALIGN_SLICE != null;
  }

  static ByteBuffer alignSlice(ByteBuffer buffer, int alignment) {
    try {
      return (ByteBuffer) ALIGN_SLICE.invoke(buffer, alignment);
    } catch (IllegalAccessException e) {
      throw new Error(e);
    } catch (InvocationTargetException e) {
      throw new Error(e);
    }
  }

  static int addressSize() {
    return UNSAFE.addressSize();
  }

  static ByteBuffer allocateDirectNoCleaner0(int capacity) {
    return newDirectBuffer(allocateMemory(Math.max(1, capacity)), capacity);
  }

  static ByteBuffer newDirectBuffer(long address, int capacity) {
    try {
      return (ByteBuffer) DIRECT_BUFFER_CONSTRUCTOR.newInstance(address, capacity);
    } catch (Throwable cause) {
      // Not expected to ever throw!
      if (cause instanceof Error) {
        throw (Error) cause;
      }
      throw new Error(cause);
    }
  }

  static long allocateMemory(long size) {
    return UNSAFE.allocateMemory(size);
  }

  static void freeMemory(long address) {
    UNSAFE.freeMemory(address);
  }

  static long reallocateMemory(long address, long newSize) {
    return UNSAFE.reallocateMemory(address, newSize);
  }

  static void putByte(long address, byte value) {
    UNSAFE.putByte(address, value);
  }

  static void putShort(long address, short value) {
    UNSAFE.putShort(address, value);
  }

  static void putInt(long address, int value) {
    UNSAFE.putInt(address, value);
  }

  static void putLong(long address, long value) {
    UNSAFE.putLong(address, value);
  }

  static void putByte(byte[] data, int index, byte value) {
    UNSAFE.putByte(data, BYTE_ARRAY_BASE_OFFSET + index, value);
  }

  static void putByte(Object data, long offset, byte value) {
    UNSAFE.putByte(data, offset, value);
  }

  static void putShort(byte[] data, int index, short value) {
    UNSAFE.putShort(data, BYTE_ARRAY_BASE_OFFSET + index, value);
  }

  static void putShort(Object data, long offset, short value) {
    UNSAFE.putShort(data, offset, value);
  }

  static void putChar(Object data, long offset, char value) {
    UNSAFE.putChar(data, offset, value);
  }

  static void putInt(byte[] data, int index, int value) {
    UNSAFE.putInt(data, BYTE_ARRAY_BASE_OFFSET + index, value);
  }

  static void putInt(Object data, long offset, int value) {
    UNSAFE.putInt(data, offset, value);
  }

  static void putFloat(Object data, long offset, float value) {
    UNSAFE.putFloat(data, offset, value);
  }

  static void putLong(byte[] data, int index, long value) {
    UNSAFE.putLong(data, BYTE_ARRAY_BASE_OFFSET + index, value);
  }

  static void putLong(Object data, long offset, long value) {
    UNSAFE.putLong(data, offset, value);
  }

  static void putDouble(Object data, long offset, double value) {
    UNSAFE.putDouble(data, offset, value);
  }

  static void putObject(Object o, long offset, Object x) {
    UNSAFE.putObject(o, offset, x);
  }

  private static final class AtomicLongCounter extends AtomicLong implements LongCounter {

    private static final long serialVersionUID = -1525255344648529377L;

    @Override
    public void add(long delta) {
      addAndGet(delta);
    }

    @Override
    public void increment() {
      incrementAndGet();
    }

    @Override
    public void decrement() {
      decrementAndGet();
    }

    @Override
    public long value() {
      return get();
    }
  }

  private static class LongAdderCounter extends LongAdder implements LongCounter {
    @Override
    public long value() {
      return longValue();
    }
  }
}
